#include "LZOInclude\lzo\lzoconf.h"
#include "LZOInclude\lzo\lzo1x.h"

#include "LZOCompressor.h"
#include "LZOCompressorException.h"

using namespace System;
using namespace System::Text;
using namespace WaveAccess::LZONet;

LZOCompressor::LZOCompressor()
{
	Initiate(CompressionAlgorithm::LZO1X1);
}

LZOCompressor::LZOCompressor(CompressionAlgorithm algoritm)
{
	Initiate(algoritm);
}

void LZOCompressor::Initiate(CompressionAlgorithm algoritm)
{
	if (lzo_init() != LZO_E_OK)
	{
		StringBuilder^ sb = gcnew StringBuilder();
		sb->AppendLine("Internal error - lzo_init() failed.");
		sb->AppendLine("This usually indicates a compiler bug. Try recompiling without optimizations, and enable '-DLZO_DEBUG' for diagnostics.");
		throw gcnew LZOCompressorException(sb->ToString());
	}
	m_CompressionAlgorithm = algoritm;
	m_CompressFunction = (pCompressDecompressFunction)m_LZOCompressorHelper.CompressFunctionTable[m_CompressionAlgorithm].ToPointer();
	Console::WriteLine("LZONet successfully initiated.");
}

array<Byte>^ LZOCompressor::Compress(array<Byte>^ srcArray)
{
	byte* workMemoryPtr = new byte[m_LZOCompressorHelper.WorkMemorySizeTable[m_CompressionAlgorithm]];
	array<Byte> ^dstArray = gcnew array<Byte>(srcArray->Length + srcArray->Length / 64 + 16 + 3 + 4);
	lzo_uint dstLength = dstArray->Length;
	lzo_uint srcLength = srcArray->Length;
	pin_ptr<Byte> srcPinPtr =  &srcArray[0]; Byte* srcPtr = srcPinPtr;
	pin_ptr<Byte> dstPinPtr =  &dstArray[0]; Byte* dstPtr = dstPinPtr;

	int retCode = m_CompressFunction(srcPtr, srcLength, dstPtr, &dstLength, workMemoryPtr);
	if (retCode != LZO_E_OK)
	{
		String^ error =  m_LZOCompressorHelper.ResolveCompressDecompressError(retCode);
		String^ message = String::Format("Internal error - compression failed. Return code: {0}. Description: {1}.", retCode, error);
		throw gcnew LZOCompressorException(message);
	}

    if (dstLength >= srcLength)
		/* check for an incompressible block */
        throw gcnew LZOCompressorException("Compression failed. Description: This block contains incompressible data.");

	delete[] workMemoryPtr;
    Array::Resize(dstArray, (int)dstLength + 4);
	Array::Copy(BitConverter::GetBytes(srcArray->Length), 0, dstArray, (int)dstLength, 4);
	return dstArray;
}

array<Byte>^ LZOCompressor::Decompress(array<Byte>^ src)
{
	return Decompress(src, true);
}

array<Byte>^ LZOCompressor::Decompress(array<Byte>^ srcArray, bool safe)
{
	array<Byte> ^dstArray = gcnew array<Byte>(BitConverter::ToInt32(srcArray, srcArray->Length - 4));
	lzo_uint dstLength = dstArray->Length;
	lzo_uint srcLength = srcArray->Length - 4;
	pin_ptr<Byte> srcPinPtr =  &srcArray[0]; Byte* srcPtr = srcPinPtr;
	pin_ptr<Byte> dstPinPtr =  &dstArray[0]; Byte* dstPtr = dstPinPtr;

	if(!safe)
		m_DecompressFunction = (pCompressDecompressFunction)m_LZOCompressorHelper.DecompressFunctionTable[m_CompressionAlgorithm].ToPointer();
	else
		m_DecompressFunction = (pCompressDecompressFunction)m_LZOCompressorHelper.DecompressSafeFunctionTable[m_CompressionAlgorithm].ToPointer();

	int retCode = m_DecompressFunction(srcPtr, srcLength, dstPtr, &dstLength, NULL);
	if (retCode != LZO_E_OK)
	{
		String^ error =  m_LZOCompressorHelper.ResolveCompressDecompressError(retCode);
		String^ message = String::Format("Internal error - decompression failed. Return code: {0}. Description: {1}.", retCode, error);
		throw gcnew LZOCompressorException(message);
	}

	return dstArray;
}

String^ LZOCompressor::ToString() 
{
    return FullVersion;
}
